#ifndef _FXMATH_MODULEMGR_
#define _FXMATH_MODULEMGR_

//*****************************************************************************
//* Module manager
//*****************************************************************************
class CFX_ModuleMgr
{
public:
	virtual ~CFX_ModuleMgr() {}

	virtual FX_BOOL		Initialize() = 0;
	virtual void		Finalize() = 0;
	virtual void		Purge() = 0;
};

#if _FX_OS_ == _FX_WIN32_DESKTOP_ || _FX_OS_ == _FX_WIN64_ || \
	_FX_OS_ == _FX_LINUX_DESKTOP_ || _FX_OS_ == _FX_MACOSX_ || _FX_OS_ == _FX_SOLARIS_ || _FX_OS_ == _FX_ANDROID_

	/** @brief Initial memory size of fixed memory manager, 16MB. */
	#define FX_FIXEDMEM_INITSIZE	(1024 * 1024 * 16)

	/** @brief Common module manager for desktop platforms. */
	class CFX_ModuleMgr_Common : public CFX_ModuleMgr
	{
	public:
		CFX_ModuleMgr_Common(size_t nInitSize = FX_FIXEDMEM_INITSIZE, FX_BOOL bFixedSize = FALSE, void* pListener = NULL)
			: m_nInitSize(nInitSize)
			, m_nMemCount(0)
			, m_bFixedSize(bFixedSize)
			, m_pFixedMemory(NULL)
			, m_pListener(pListener)
		{
			Initialize();
		}
		virtual ~CFX_ModuleMgr_Common()
		{
			Finalize();
		}

		virtual FX_BOOL		Initialize()
		{
			if (m_pFixedMemory) return FALSE;
			m_nMemCount = 0;

			if (!m_nInitSize)
				FXMEM_GetDefaultMgr();
			else
			{
			#if 1
				m_pFixedMemory = malloc(m_nInitSize);
				m_SystemMgr.More = &Common_More;
				m_SystemMgr.Free = &Common_Free;
				#if defined(_FX_MEMLISTENER_)
					FXMEM_SetFixedMgrListener(m_pListener);
				#endif
				FXMEM_FoxitMgr *pFixedMgr = FXMEM_CreateFixedMgr(m_pFixedMemory, m_nInitSize, m_bFixedSize ? NULL : &m_SystemMgr);
				FXMEM_SetDefaultMgr(pFixedMgr);
			#else
				FXMEM_FoxitMgr *pFixedMgr = FXMEM_CreateMemoryMgr(1024 * 1024 * 32, TRUE);
			#endif
				FXMEM_SetOOMHandler(pFixedMgr, &Common_OOMHandler, NULL);
			}
			FX_Process_Initialize();

			CCodec_ModuleMgr* pCodecMgr = CCodec_ModuleMgr::Create();
			CFX_GEModule::Create();
			CFX_GEModule::Get()->SetCodecModule(pCodecMgr);
			CPDF_ModuleMgr::Create();
			CPDF_ModuleMgr* pPDFMgr = CPDF_ModuleMgr::Get();
			pPDFMgr->SetCodecModule(pCodecMgr);
			pPDFMgr->InitPageModule();
			pPDFMgr->InitRenderModule();
			pPDFMgr->LoadEmbeddedGB1CMaps();
			pPDFMgr->LoadEmbeddedCNS1CMaps();
			pPDFMgr->LoadEmbeddedJapan1CMaps();
			pPDFMgr->LoadEmbeddedKorea1CMaps();

			return TRUE;
		}

		virtual void		Finalize()
		{
			CCodec_ModuleMgr* pCodecMgr = CPDF_ModuleMgr::Get()->GetCodecModule();
			CPDF_ModuleMgr::Destroy();
			CFX_GEModule::Destroy();
			pCodecMgr->Destroy();

			Purge();
			FX_Process_Finalize();
			FXMEM_DestroyFoxitMgr(FXMEM_GetDefaultMgr());
			if (m_pFixedMemory)
			{
				free(m_pFixedMemory);
				m_pFixedMemory = NULL;
			}
		}

		virtual void		Purge()
		{
			CFX_GEModule *pGEModule = CFX_GEModule::Get();
			if (pGEModule)
			{
				CFX_FontCache *pFontCache = pGEModule->GetFontCache();
				if (pFontCache) pFontCache->FreeCache();
			}
			FXMEM_PurgeMgr(FXMEM_GetDefaultMgr());
		}

	protected:
		size_t				m_nInitSize;
		size_t				m_nMemCount;
		FX_BOOL				m_bFixedSize;
		FX_LPVOID			m_pFixedMemory;
		FXMEM_SystemMgr2	m_SystemMgr;
		void*				m_pListener;

		static FX_BOOL Common_More(FXMEM_SystemMgr2* pMgr, size_t alloc_size, void** new_memory, size_t* new_size)
		{
		#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
			CFX_ModuleMgr_Common* pMMC = (CFX_ModuleMgr_Common*)((FX_LPBYTE)pMgr - offsetof(CFX_ModuleMgr_Common, m_SystemMgr));
			pMMC->m_nMemCount ++;
		#endif
			*new_size = alloc_size;
			*new_memory = malloc(*new_size);
		#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
			char info[128];
			sprintf(info, "\r\nNew memory allocated, size = %d, address = 0x%X, memCount = %d\r\n", new_size, (FX_UINTPTR)*new_memory, pMMC->m_nMemCount);
			::OutputDebugStringA(info);
		#endif
			return *new_memory != NULL;
		}

		static void Common_Free(FXMEM_SystemMgr2* pMgr, void* memory)
		{
		#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
			CFX_ModuleMgr_Common* pMMC = (CFX_ModuleMgr_Common*)((FX_LPBYTE)pMgr - offsetof(CFX_ModuleMgr_Common, m_SystemMgr));
			pMMC->m_nMemCount --;
		#endif
			free(memory);
		#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
			char info[128];
			sprintf(info, "\r\nA memory freed, address = 0x%X, memCount = %d\r\n", (FX_UINTPTR)memory, pMMC->m_nMemCount);
			::OutputDebugStringA(info);
		#endif
		}

		static void Common_OOMHandler(FXMEM_FoxitMgr* pFoxitMgr, void* param)
		{
			FXMEM_DestroyFoxitMgr(FXMEM_GetDefaultMgr());
			exit(0);
		}
	};

#endif

#endif //_FXMATH_MODULEMGR_
